///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_MAT_EXPRESSIONS_H_
#define _BUOLA_MAT_EXPRESSIONS_H_

#include <buola/mat/detail/ctranspose.h>
#include <buola/mat/detail/cinverse.h>
#include <buola/mat/detail/cblock.h>
#include <buola/mat/detail/cdiagonal.h>
#include <buola/mat/detail/cdiagonalmatrix.h>
#include <buola/mat/detail/ccolselection.h>
#include <buola/mat/detail/crowselection.h>
#include <buola/mat/detail/cpickselection.h>
#include <buola/mat/detail/ccolextension.h>
#include <buola/mat/detail/crowextension.h>
#include <buola/mat/detail/cscalarextension.h>

namespace buola { namespace mat {

template<typename tDerived>
detail::CTranspose<detail::OConstNestType<tDerived>> CExpr<tDerived>::T() const
{
    return {Derived()};
}

template<typename tDerived>
detail::CTranspose<detail::ONonConstNestType<tDerived>> CExpr<tDerived>::T()
{
    return {Derived()};
}

template<typename tDerived>
detail::CInverse<detail::OConstNestType<tDerived>> CExpr<tDerived>::I() const
{
    return {Derived()};
}

template<typename tDerived>
detail::CBlock<detail::OConstNestType<tDerived>,1,detail::MMatrixTraits<tDerived>::cCols,true,false> 
            CExpr<tDerived>::operator()(size_t r,NAll) const
{
    return {Derived(),1,Derived().Cols(),r,0};
}

template<typename tDerived>
detail::CBlock<detail::ONonConstNestType<tDerived>,1,detail::MMatrixTraits<tDerived>::cCols,true,false>
            CExpr<tDerived>::operator()(size_t r,NAll)
{
    return {Derived(),1,Derived().Cols(),r,0};
}

template<typename tDerived>
detail::CBlock<detail::OConstNestType<tDerived>,detail::MMatrixTraits<tDerived>::cRows,1,false,true> 
            CExpr<tDerived>::operator()(NAll,size_t c) const
{
    return {Derived(),Derived().Rows(),1,0,c};
}

template<typename tDerived>
detail::CBlock<detail::ONonConstNestType<tDerived>,detail::MMatrixTraits<tDerived>::cRows,1,false,true>
            CExpr<tDerived>::operator()(NAll,size_t c)
{
    return {Derived(),Derived().Rows(),1,0,c};
}

template<typename tDerived>
detail::CBlock<detail::OConstNestType<tDerived>,detail::MMatrixTraits<tDerived>::cRows,cDynamic,false,true> 
            CExpr<tDerived>::operator()(NAll,size_t pFC,size_t pNC) const
{
    return {Derived(),Derived().Rows(),pNC,0,pFC};
}

template<typename tDerived>
detail::CBlock<detail::ONonConstNestType<tDerived>,detail::MMatrixTraits<tDerived>::cRows,cDynamic,false,true> 
            CExpr<tDerived>::operator()(NAll,size_t pFC,size_t pNC)
{
    return {Derived(),Derived().Rows(),pNC,0,pFC};
}

template<typename tDerived>
detail::CBlock<detail::OConstNestType<tDerived>,cDynamic,detail::MMatrixTraits<tDerived>::cCols,false,true> 
            CExpr<tDerived>::operator()(size_t pFR,size_t pNR,NAll) const
{
    return {Derived(),pNR,Derived().Cols(),pFR,0};
}

template<typename tDerived>
detail::CBlock<detail::ONonConstNestType<tDerived>,cDynamic,detail::MMatrixTraits<tDerived>::cCols,false,true> 
            CExpr<tDerived>::operator()(size_t pFR,size_t pNR,NAll)
{
    return {Derived(),pNR,Derived().Cols(),pFR,0};
}

template<typename tDerived>
detail::CDiagonal<detail::OConstNestType<tDerived>> diag(const CExpr<tDerived> &pExpr)
{
    return {pExpr.Derived()};
}

template<typename tDerived>
detail::CDiagonal<detail::ONonConstNestType<tDerived>> diag(CExpr<tDerived> &pExpr)
{
    return {pExpr.Derived()};
}

template<typename tDerived>
detail::CDiagonalMatrix<detail::OConstNestType<tDerived>> diagm(const CExpr<tDerived> &pExpr)
{
    return {pExpr.Derived()};
}

template<typename tDerived>
detail::CDiagonalMatrix<detail::ONonConstNestType<tDerived>> diagm(CExpr<tDerived> &pExpr)
{
    return {pExpr.Derived()};
}

template<typename tSelection,OEnableIf<detail::QBooleanVector<tSelection>> =0>
std::vector<size_t> indices(const CExpr<tSelection> &pSelection)
{
    std::vector<size_t> lIndices;
    const auto &lSelection=detail::element_accessible<detail::ACCESS_ONCE>(pSelection.Derived());
    for(int i=0;i<lSelection.size();i++)
        if(lSelection[i])
            lIndices.push_back(i);
    return lIndices;
}

template<typename tDerived>
template<typename tSelection,OEnableIf<detail::QBooleanVector<tSelection>>>
detail::CColSelection<const tDerived&,std::vector<size_t>> CExpr<tDerived>::operator()(NAll,const CExpr<tSelection> &pSelection)
{
    return {Derived(),indices(pSelection)};
}

template<typename tDerived>
template<typename tSelection,OEnableIf<detail::QBooleanVector<tSelection>>>
detail::CRowSelection<const tDerived&,std::vector<size_t>> CExpr<tDerived>::operator()(const CExpr<tSelection> &pSelection,NAll)
{
    return {Derived(),indices(pSelection)};
}

template<typename tDerived>
detail::CColSelection<const tDerived&,std::vector<size_t>> CExpr<tDerived>::operator()(NAll,const std::vector<size_t> &pSelection)
{
    return {Derived(),pSelection};
}

template<typename tDerived>
detail::CRowSelection<const tDerived&,std::vector<size_t>> CExpr<tDerived>::operator()(const std::vector<size_t> &pSelection,NAll)
{
    return {Derived(),pSelection};
}

template<typename tDerived>
template<typename tPick,OEnableIf<detail::QBoolean<tPick>>>
detail::CPickSelection<detail::OConstNestType<tDerived>,const tPick&> CExpr<tDerived>::operator[](const CExpr<tPick> &pPick) const
{
    return {Derived(),pPick.Derived()};
}

template<typename tDerived>
template<typename tPick,OEnableIf<detail::QBoolean<tPick>>>
detail::CPickSelection<detail::ONonConstNestType<tDerived>,const tPick&> CExpr<tDerived>::operator[](const CExpr<tPick> &pPick)
{
    return {Derived(),pPick.Derived()};
}

template<typename tDerived>
OEnableIf<detail::QRowVector<tDerived>,detail::OColExtensionResult<const tDerived&>> extend(const CExpr<tDerived> &pE)
{
    return {pE.Derived()};
}

template<typename tDerived>
OEnableIf<detail::QColVector<tDerived>,detail::ORowExtensionResult<const tDerived&>> extend(const CExpr<tDerived> &pE)
{
    return {pE.Derived()};
}

template<typename tScalar>
OEnableIf<QIsNumeric<tScalar>,detail::CScalarExtension<tScalar>> extend(const tScalar &pScalar)
{
    return {pScalar};
}

/*namespace mat*/ } /*namespace buola*/ }

#endif
